﻿using System;
using System.IO;
using System.Security.Cryptography;
using EncryptionLib.Users;
using EncryptionLib.Util;

namespace EncryptionLib.Signature
{
    public class Signer
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="sPrivateKey">Private key of author. Get from <see cref="UserData.GetPlainPrivateKey()"></see>/></param>
        /// <param name="sSourceFile"></param>
        /// <param name="sSignatureFile"></param>
        public static void Sign(string sPrivateKey, string sSourceFile, 
            string sSignatureFile)
        {
            // hash data
            HashAlgorithm hasher;
            //hasher = new SHA256Managed();
            hasher = new SHA1Managed();
            byte[] hashVal = null;
            using (FileStream f = new FileStream(sSourceFile, FileMode.Open, FileAccess.Read))
            {
                hashVal = hasher.ComputeHash(f);
            }
            if (hashVal == null)
            {
                throw new Exception("Cannot create hash value.");
            }

            // encrypt it with private key
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
            rsa.FromXmlString(sPrivateKey);
            //////////////////
            //bug !!
            //byte[] signVal = rsa.SignHash(hashVal, CryptoConfig.MapNameToOID("SHA256"));
            byte[] signVal = rsa.SignHash(hashVal, CryptoConfig.MapNameToOID("SHA1"));

            // write the signature to the output file
            File.WriteAllBytes(sSignatureFile, signVal);
        }

        public static void Sign(string sPrivateKey, string sSourceFile)
        {
            string sSigFile = Path.Combine(Path.GetDirectoryName(sSourceFile), Path.GetFileName(sSourceFile) + ".sig");
            Sign(sPrivateKey, sSourceFile, sSigFile);
        }

        /// <summary>
        /// Before call this function, caller have to check if Author has 
        /// input a proper Passphrase.
        /// </summary>
        /// <param name="Author">Author of the signature.</param>
        /// <param name="arrAuthorPassphrase">Passphrase of the author.</param>
        /// <param name="sSourceFile">File to be signed.</param>
        /// <param name="sSignatureFile">File that contains the signature.</param>
        public static void Sign(UserData Author, byte[] arrAuthorPassphrase, 
            string sSourceFile, string sSignatureFile)
        {
            Sign(Author.GetPlainPrivateKey(arrAuthorPassphrase),
                sSourceFile, sSignatureFile);
        }

        public static void Sign(UserData Author, string sAuthorPassphrase,
            string sSourceFile, string sSignatureFile)
        {
            Sign(Author.GetPlainPrivateKey(SerializerHelper.String2Bytes(sAuthorPassphrase)),
                sSourceFile, sSignatureFile);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="Author"></param>
        /// <param name="sAuthorPassphrase"></param>
        /// <param name="sSourceFile"></param>
        public static void Sign(UserData Author, string sAuthorPassphrase, 
            string sSourceFile)
        {
            Sign(Author.GetPlainPrivateKey(SerializerHelper.String2Bytes(sAuthorPassphrase)),
                sSourceFile);
        }

        public static bool Verify(string sPublicKey, string sDataFile, 
            string sSignatureFile)
        {
            HashAlgorithm hasher;
            //hasher = new SHA256Managed();
            hasher = new SHA1Managed();

            byte[] hashVal = null;
            using (FileStream f = new FileStream(sDataFile, FileMode.Open, FileAccess.Read))
            {
                hashVal = hasher.ComputeHash(f);
            }
            if (hashVal == null)
            {
                throw new Exception("Cannot create hash value.");
            }

            // decryption the signature
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
            rsa.FromXmlString(sPublicKey);

            return rsa.VerifyHash(hashVal,
                //CryptoConfig.MapNameToOID("SHA256"),
                CryptoConfig.MapNameToOID("SHA1"),
                File.ReadAllBytes(sSignatureFile));
        }

        public static bool Verify(UserData Author, string sDataFile, string sSignatureFile)
        {
            return Verify(Author.PublicKey,
                sDataFile, sSignatureFile);
        }
    }
}
